﻿using System;
using System.IO;
using System.Text;
using GeoAPI.Geometries;
using GeoAPI.IO;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.IO
{
    public class PrivateImplementationDetails
    {
        public static uint ComputeStringHash(string s)
        {
            uint num = new uint();
            if (s != null)
            {
                num = 0x811c9dc5;
                for (int i = 0; i < s.Length; i++)
                {
                    num = (s[i] ^ num) * 0x1000193;
                }
            }
            return num;
        }
    }

	public class WKBWriter : IBinaryGeometryWriter, IGeometryWriter<byte[]>, IGeometryIOSettings
	{
		[Obsolete("Use ToHex(byte[])")]
		public static string BytesToHex(byte[] bytes)
		{
			return WKBWriter.ToHex(bytes);
		}

		public static string ToHex(byte[] bytes)
		{
			StringBuilder stringBuilder = new StringBuilder(bytes.Length * 2);
			foreach (byte b in bytes)
			{
				stringBuilder.Append(WKBWriter.ToHexDigit(b >> 4 & 15));
				stringBuilder.Append(WKBWriter.ToHexDigit((int)(b & 15)));
			}
			return stringBuilder.ToString();
		}

		private static char ToHexDigit(int n)
		{
			if (n < 0 || n > 15)
			{
				throw new ArgumentException("Nibble value out of range: " + n);
			}
			if (n <= 9)
			{
				return (char)(48 + n);
			}
			return (char)(65 + (n - 10));
		}

		[Obsolete("Use HandleSRID instead")]
		public bool EmitSRID
		{
			get
			{
				return this.HandleSRID;
			}
			set
			{
				this.HandleSRID = value;
			}
		}

		[Obsolete("Use HandleOrdinates instead")]
		public bool EmitZ
		{
			get
			{
				return this._emitZ;
			}
			set
			{
				if (value == this._emitZ)
				{
					return;
				}
				this._emitZ = value;
				if (value)
				{
					this.HandleOrdinates |= Ordinates.Z;
				}
				else
				{
					this.HandleOrdinates &= ~Ordinates.Z;
				}
				this.CalcCoordinateSize();
			}
		}

		[Obsolete("Use HandleOrdintes instead.")]
		public bool EmitM
		{
			get
			{
				return this._emitM;
			}
			set
			{
				if (value == this._emitM)
				{
					return;
				}
				this._emitM = value;
				if (value)
				{
					this.HandleOrdinates |= Ordinates.M;
				}
				else
				{
					this.HandleOrdinates &= ~Ordinates.M;
				}
				this.CalcCoordinateSize();
			}
		}

		private void WriteHeader(BinaryWriter writer, IGeometry geom)
		{
			this.WriteByteOrder(writer);
			string geometryType = geom.GeometryType;
			uint num = PrivateImplementationDetails.ComputeStringHash(geometryType);
			WKBGeometryTypes wkbgeometryTypes;
			if (num <= 3674500762u)
			{
				if (num <= 2050635977u)
				{
					if (num != 1547714260u)
					{
						if (num != 2050635977u)
						{
							goto IL_121;
						}
						if (!(geometryType == "MultiLineString"))
						{
							goto IL_121;
						}
						wkbgeometryTypes = WKBGeometryTypes.WKBMultiLineString;
						goto IL_141;
					}
					else
					{
						if (!(geometryType == "MultiPolygon"))
						{
							goto IL_121;
						}
						wkbgeometryTypes = WKBGeometryTypes.WKBMultiPolygon;
						goto IL_141;
					}
				}
				else if (num != 2386032169u)
				{
					if (num != 3674500762u)
					{
						goto IL_121;
					}
					if (!(geometryType == "LinearRing"))
					{
						goto IL_121;
					}
				}
				else
				{
					if (!(geometryType == "Polygon"))
					{
						goto IL_121;
					}
					wkbgeometryTypes = WKBGeometryTypes.WKBPolygon;
					goto IL_141;
				}
			}
			else if (num <= 3786658501u)
			{
				if (num != 3694217412u)
				{
					if (num != 3786658501u)
					{
						goto IL_121;
					}
					if (!(geometryType == "GeometryCollection"))
					{
						goto IL_121;
					}
					wkbgeometryTypes = WKBGeometryTypes.WKBGeometryCollection;
					goto IL_141;
				}
				else
				{
					if (!(geometryType == "MultiPoint"))
					{
						goto IL_121;
					}
					wkbgeometryTypes = WKBGeometryTypes.WKBMultiPoint;
					goto IL_141;
				}
			}
			else if (num != 3936939825u)
			{
				if (num != 4094167858u)
				{
					goto IL_121;
				}
				if (!(geometryType == "LineString"))
				{
					goto IL_121;
				}
			}
			else
			{
				if (!(geometryType == "Point"))
				{
					goto IL_121;
				}
				wkbgeometryTypes = WKBGeometryTypes.WKBPoint;
				goto IL_141;
			}
			wkbgeometryTypes = WKBGeometryTypes.WKBLineString;
			goto IL_141;
			IL_121:
			Assert.ShouldNeverReachHere("Unknown geometry type:" + geom.GeometryType);
			throw new ArgumentException("geom");
			IL_141:
			uint num2 = (uint)(wkbgeometryTypes & (WKBGeometryTypes)255);
			if ((this.HandleOrdinates & Ordinates.Z) == Ordinates.Z)
			{
				num2 += 1000u;
				if (!this.Strict)
				{
					num2 |= 2147483648u;
				}
			}
			if ((this.HandleOrdinates & Ordinates.M) == Ordinates.M)
			{
				num2 += 2000u;
				if (!this.Strict)
				{
					num2 |= 1073741824u;
				}
			}
			if (this.HandleSRID)
			{
				num2 |= 536870912u;
			}
			writer.Write(num2);
			if (this.HandleSRID)
			{
				writer.Write(geom.SRID);
			}
		}

		protected int InitCount
		{
			get
			{
				return 5 + (this.HandleSRID ? 4 : 0);
			}
		}

		public WKBWriter() : this(ByteOrder.LittleEndian, false)
		{
		}

		public WKBWriter(ByteOrder encodingType) : this(encodingType, false)
		{
		}

		public WKBWriter(ByteOrder encodingType, bool handleSRID) : this(encodingType, handleSRID, false)
		{
		}

		public WKBWriter(ByteOrder encodingType, bool handleSRID, bool emitZ) : this(encodingType, handleSRID, emitZ, false)
		{
		}

		public WKBWriter(ByteOrder encodingType, bool handleSRID, bool emitZ, bool emitM)
		{
			this.EncodingType = encodingType;
			if (handleSRID)
			{
				this._strict = false;
			}
			this.HandleSRID = handleSRID;
			Ordinates ordinates = Ordinates.XY;
			if (emitZ)
			{
				ordinates |= Ordinates.Z;
			}
			if (emitM)
			{
				ordinates |= Ordinates.M;
			}
			this._handleOrdinates = ordinates;
			this.CalcCoordinateSize();
		}

		public virtual byte[] Write(IGeometry geometry)
		{
			byte[] bytes = this.GetBytes(geometry);
			this.Write(geometry, new MemoryStream(bytes));
			return bytes;
		}

		public virtual void Write(IGeometry geometry, Stream stream)
		{
			BinaryWriter binaryWriter = null;
			try
			{
				binaryWriter = ((this.EncodingType == ByteOrder.LittleEndian) ? new BinaryWriter(stream) : new BEBinaryWriter(stream));
				this.Write(geometry, binaryWriter);
			}
			finally
			{
				if (binaryWriter != null)
				{
					((IDisposable)binaryWriter).Dispose();
				}
			}
		}

		protected void Write(IGeometry geometry, BinaryWriter writer)
		{
			if (geometry is IPoint)
			{
				this.Write(geometry as IPoint, writer);
				return;
			}
			if (geometry is ILineString)
			{
				this.Write(geometry as ILineString, writer);
				return;
			}
			if (geometry is IPolygon)
			{
				this.Write(geometry as IPolygon, writer);
				return;
			}
			if (geometry is IMultiPoint)
			{
				this.Write(geometry as IMultiPoint, writer);
				return;
			}
			if (geometry is IMultiLineString)
			{
				this.Write(geometry as IMultiLineString, writer);
				return;
			}
			if (geometry is IMultiPolygon)
			{
				this.Write(geometry as IMultiPolygon, writer);
				return;
			}
			if (geometry is IGeometryCollection)
			{
				this.Write(geometry as IGeometryCollection, writer);
				return;
			}
			throw new ArgumentException("Geometry not recognized: " + geometry);
		}

		protected void WriteByteOrder(BinaryWriter writer)
		{
			writer.Write((byte)this.EncodingType);
		}

		protected void Write(Coordinate coordinate, BinaryWriter writer)
		{
			writer.Write(coordinate.X);
			writer.Write(coordinate.Y);
			if ((this.HandleOrdinates & Ordinates.Z) == Ordinates.Z)
			{
				writer.Write(coordinate.Z);
			}
			if ((this.HandleOrdinates & Ordinates.M) == Ordinates.M)
			{
				writer.Write(double.NaN);
			}
		}

		protected void Write(ICoordinateSequence sequence, bool emitSize, BinaryWriter writer)
		{
			if (emitSize)
			{
				writer.Write(sequence.Count);
			}
			double value = double.NaN;
			double value2 = double.NaN;
			bool flag = (sequence.Ordinates & Ordinates.Z) == Ordinates.Z;
			bool flag2 = (sequence.Ordinates & Ordinates.M) == Ordinates.M;
			bool flag3 = (this.HandleOrdinates & Ordinates.Z) == Ordinates.Z;
			bool flag4 = (this.HandleOrdinates & Ordinates.M) == Ordinates.M;
			for (int i = 0; i < sequence.Count; i++)
			{
				writer.Write(sequence.GetOrdinate(i, Ordinate.X));
				writer.Write(sequence.GetOrdinate(i, Ordinate.Y));
				if (flag3)
				{
					if (flag)
					{
						value = sequence.GetOrdinate(i, Ordinate.Z);
					}
					writer.Write(value);
				}
				if (flag4)
				{
					if (flag2)
					{
						value2 = sequence.GetOrdinate(i, Ordinate.M);
					}
					writer.Write(value2);
				}
			}
		}

		protected void Write(IPoint point, BinaryWriter writer)
		{
			this.WriteHeader(writer, point);
			this.Write(point.CoordinateSequence, false, writer);
		}

		protected void Write(ILineString lineString, BinaryWriter writer)
		{
			this.WriteHeader(writer, lineString);
			this.Write(lineString.CoordinateSequence, true, writer);
		}

		protected void Write(ILinearRing ring, BinaryWriter writer)
		{
			this.Write(ring.CoordinateSequence, true, writer);
		}

		protected void Write(IPolygon polygon, BinaryWriter writer)
		{
			this.WriteHeader(writer, polygon);
			writer.Write(polygon.NumInteriorRings + 1);
			this.Write(polygon.ExteriorRing as ILinearRing, writer);
			for (int i = 0; i < polygon.NumInteriorRings; i++)
			{
				this.Write(polygon.InteriorRings[i] as ILinearRing, writer);
			}
		}

		protected void Write(IMultiPoint multiPoint, BinaryWriter writer)
		{
			this.WriteHeader(writer, multiPoint);
			writer.Write(multiPoint.NumGeometries);
			for (int i = 0; i < multiPoint.NumGeometries; i++)
			{
				this.Write(multiPoint.Geometries[i] as IPoint, writer);
			}
		}

		protected void Write(IMultiLineString multiLineString, BinaryWriter writer)
		{
			this.WriteHeader(writer, multiLineString);
			writer.Write(multiLineString.NumGeometries);
			for (int i = 0; i < multiLineString.NumGeometries; i++)
			{
				this.Write(multiLineString.Geometries[i] as ILineString, writer);
			}
		}

		protected void Write(IMultiPolygon multiPolygon, BinaryWriter writer)
		{
			this.WriteHeader(writer, multiPolygon);
			writer.Write(multiPolygon.NumGeometries);
			for (int i = 0; i < multiPolygon.NumGeometries; i++)
			{
				this.Write(multiPolygon.Geometries[i] as IPolygon, writer);
			}
		}

		protected void Write(IGeometryCollection geomCollection, BinaryWriter writer)
		{
			this.WriteHeader(writer, geomCollection);
			writer.Write(geomCollection.NumGeometries);
			for (int i = 0; i < geomCollection.NumGeometries; i++)
			{
				this.Write(geomCollection.Geometries[i], writer);
			}
		}

		protected byte[] GetBytes(IGeometry geometry)
		{
			if (geometry is IPoint)
			{
				return new byte[this.SetByteStream(geometry as IPoint)];
			}
			if (geometry is ILineString)
			{
				return new byte[this.SetByteStream(geometry as ILineString)];
			}
			if (geometry is IPolygon)
			{
				return new byte[this.SetByteStream(geometry as IPolygon)];
			}
			if (geometry is IMultiPoint)
			{
				return new byte[this.SetByteStream(geometry as IMultiPoint)];
			}
			if (geometry is IMultiLineString)
			{
				return new byte[this.SetByteStream(geometry as IMultiLineString)];
			}
			if (geometry is IMultiPolygon)
			{
				return new byte[this.SetByteStream(geometry as IMultiPolygon)];
			}
			if (geometry is IGeometryCollection)
			{
				return new byte[this.SetByteStream(geometry as IGeometryCollection)];
			}
			throw new ArgumentException("ShouldNeverReachHere");
		}

		protected virtual int SetByteStream(IGeometry geometry)
		{
			if (geometry is IPoint)
			{
				return this.SetByteStream(geometry as IPoint);
			}
			if (geometry is ILineString)
			{
				return this.SetByteStream(geometry as ILineString);
			}
			if (geometry is IPolygon)
			{
				return this.SetByteStream(geometry as IPolygon);
			}
			if (geometry is IMultiPoint)
			{
				return this.SetByteStream(geometry as IMultiPoint);
			}
			if (geometry is IMultiLineString)
			{
				return this.SetByteStream(geometry as IMultiLineString);
			}
			if (geometry is IMultiPolygon)
			{
				return this.SetByteStream(geometry as IMultiPolygon);
			}
			if (geometry is IGeometryCollection)
			{
				return this.SetByteStream(geometry as IGeometryCollection);
			}
			throw new ArgumentException("ShouldNeverReachHere");
		}

		protected int SetByteStream(IGeometryCollection geometry)
		{
			int num = this.InitCount;
			num += 4;
			foreach (IGeometry byteStream in geometry.Geometries)
			{
				num += this.SetByteStream(byteStream);
			}
			return num;
		}

		protected int SetByteStream(IMultiPolygon geometry)
		{
			int num = this.InitCount;
			num += 4;
			foreach (IPolygon byteStream in geometry.Geometries)
			{
				num += this.SetByteStream(byteStream);
			}
			return num;
		}

		protected int SetByteStream(IMultiLineString geometry)
		{
			int num = this.InitCount;
			num += 4;
			foreach (ILineString byteStream in geometry.Geometries)
			{
				num += this.SetByteStream(byteStream);
			}
			return num;
		}

		protected int SetByteStream(IMultiPoint geometry)
		{
			int num = this.InitCount;
			num += 4;
			foreach (IPoint byteStream in geometry.Geometries)
			{
				num += this.SetByteStream(byteStream);
			}
			return num;
		}

		protected int SetByteStream(IPolygon geometry)
		{
			int coordinateSize = this._coordinateSize;
			return this.InitCount + 4 + 4 * (geometry.NumInteriorRings + 1) + geometry.NumPoints * coordinateSize;
		}

		protected int SetByteStream(ILineString geometry)
		{
			int coordinateSize = this._coordinateSize;
			int numPoints = geometry.NumPoints;
			return this.InitCount + 4 + coordinateSize * numPoints;
		}

		protected int SetByteStream(IPoint geometry)
		{
			return this.InitCount + this._coordinateSize;
		}

		private void CalcCoordinateSize()
		{
			this._coordinateSize = 16;
			if ((this.HandleOrdinates & Ordinates.Z) == Ordinates.Z)
			{
				this._coordinateSize += 8;
			}
			if ((this.HandleOrdinates & Ordinates.M) == Ordinates.M)
			{
				this._coordinateSize += 8;
			}
		}

		public bool Strict
		{
			get
			{
				return this._strict;
			}
			set
			{
				this._strict = value;
				if (this._strict)
				{
					this.HandleSRID = false;
				}
			}
		}

		public bool HandleSRID
		{
			get
			{
				return this._handleSRID;
			}
			set
			{
				if (this._strict && value)
				{
					throw new ArgumentException("Cannot set HandleSRID to true if Strict is set", "value");
				}
				this._handleSRID = value;
			}
		}

		public Ordinates AllowedOrdinates
		{
			get
			{
				return Ordinates.XYZM;
			}
		}

		public Ordinates HandleOrdinates
		{
			get
			{
				return this._handleOrdinates;
			}
			set
			{
				value = (Ordinates.XY | (this.AllowedOrdinates & value));
				if (value == this._handleOrdinates)
				{
					return;
				}
				this._handleOrdinates = value;
				this._emitZ = ((value & Ordinates.Z) > Ordinates.None);
				this._emitM = ((value & Ordinates.M) > Ordinates.None);
				this.CalcCoordinateSize();
			}
		}

		public ByteOrder ByteOrder
		{
			get
			{
				return this.EncodingType;
			}
			set
			{
			}
		}

		private bool _emitZ;

		private bool _emitM;

		protected ByteOrder EncodingType;

		private int _coordinateSize = 16;

		private Ordinates _handleOrdinates;

		private bool _handleSRID;

		private bool _strict = true;
	}
}
